package world;

import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import util.Arquivos;

public class World {

    private Map<Long, Issue> issues;
    private Map<String, Author> authors;
    private List<Message> messages;
    private static Connection con;

    private void inicializarEstruturas() {
        this.issues = new HashMap<Long, Issue>();
        this.authors = new HashMap<String, Author>();
        this.messages = new ArrayList<Message>();
    }

    public World() {
        this.inicializarEstruturas();
        try {
            conectar();

            String query = "SELECT c.id,c.tokensordered,c.tokensreduzido, c.autor,c.issue_id,c.datacomentario, i.datacriada FROM comentario c, issue i WHERE i.id=c.issue_id and i.datacriada > '2008-01-01 00:00:00'";
            PreparedStatement ps = con.prepareStatement(query);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                long id = rs.getLong("id");
                long issueId = rs.getLong("issue_id");
                String tokens = rs.getString("tokensreduzido");
                String authorName = rs.getString("autor");
                java.sql.Timestamp dataComentario = rs.getTimestamp("dataComentario");
                if (tokens != null) {
                    Message msg = new Message();
                    msg.setId(id);
                    msg.setData(dataComentario);
                    msg.setTokens(tokens);

                    // Tenta carregar um autor já existente, se não existir o método cria um e insere na lista
                    Author author = this.getAuthor(authorName);

                    // Tenta carregar uma issue já existente, se não existir o método cria uma e insere na lista
                    Issue issue = this.getIssue(issueId);

                    msg.setAutor(author);//atribui o autor da mensagem
                    msg.setIssue(issue);// atribui a issue que a mensagem está contida

                    author.insertMessage(msg);//insere a mensagem na lista de mensagens do autor
                    author.insertIssue(issue);//insere a issue na lista de issues que o autor participou

                    issue.insertMessage(msg); // insere a mensagem na lista de mensagens da issue
                    issue.insertAuthor(author); // insere o autor na lista de autores da issue

                    this.insertMessage(msg); // insere a mensagem na lista de mensagens
                }
            }
            rs.close();
            ps.close();
            desconectar();

            Collections.sort(this.messages);

        } catch (Exception ex) {
            System.err.println("Erro de conexão com o banco");
        }
    }

    public List<String> getWorld(Timestamp in, Timestamp out) {
        Set<String> worldSet = new HashSet<String>();
        for (int i = 0; i < this.messages.size(); i++) {
            Message msg = this.messages.get(i);
            if (msg.getData().compareTo(out) <= 0 && msg.getData().compareTo(in) >= 0 && msg.getTokens() != null) {
                worldSet.addAll(Arrays.asList(msg.getTokensArray()));
            }
        }
        List<String> worldList = Arrays.asList(worldSet.toArray(new String[0]));;
        Collections.sort(worldList);
        return worldList;
    }

    public void geraWeka(String authorName) {
        Author author = this.getAuthor(authorName);
        Timestamp initialDate = author.getInitialPaticipation();
        Timestamp finalDate = author.getFinalPaticipation();

        List<String> worldList = this.getWorld(initialDate, finalDate);
        List<Long> issuesIds = new ArrayList<Long>(Arrays.asList(this.issues.keySet().toArray(new Long[0])));

        this.getTrainingSet(worldList, issuesIds, author);
        System.gc();
        this.getTestSet(worldList, issuesIds, author);

    }

    // arquivo de treino 
    // 
    private void getTrainingSet(List<String> world, List<Long> issuesIds, Author author) {
        StringBuilder head = new StringBuilder();
        head.append("@relation " + author.getNome() + "\n\n");
        for (int i = 0; i < world.size(); i++) {
            head.append("@attribute ");
            head.append(i);
            head.append(" {0,1}\n");
        }
        head.append("@attribute resultado {0, 1}\n");
        head.append("\n");
        head.append("@data\n");

        String fileName = String.format("arquivosGerados/%sTraining.arff", author.getNome());
        Arquivos.gravar(fileName, head.toString(), false);


        int total = (int) (0.8 * issuesIds.size());
        System.out.println("Tamanho MiniMundo:" + world.size());
        System.out.println("N.o Issues: " + issuesIds.size());
        System.out.println("Total: " + total);
        Random gerador = new Random();
        for (int i = 0; i < total; i++) {
            StringBuilder conteudo = new StringBuilder();
            int num = gerador.nextInt(issuesIds.size());
            Long id = issuesIds.get(num);
            Issue issue = this.getIssue(id);
            boolean[] vector = issue.getVector(world);
            for (int j = 0; j < vector.length; j++) {
                conteudo.append(vector[j] ? 1 : 0).append(", ");
            }
            conteudo.append(issue.containsAuthor(author) ? 1 : 0);
            issuesIds.remove(id);
            System.gc();
            Arquivos.gravar(fileName, conteudo.toString(), true);
        }
    }

    private void getTestSet(List<String> world, List<Long> issuesIds, Author author) {
        StringBuilder head = new StringBuilder();
        head.append("@relation " + author.getNome() + "\n\n");
        for (int i = 0; i < world.size(); i++) {
            head.append("@attribute ");
            head.append(i);
            head.append(" {0,1}\n");
        }
        head.append("@attribute resultado {0, 1}\n");
        head.append("\n");
        head.append("@data\n");

        String fileName = String.format("arquivosGerados/%sTest.arff", author.getNome());
        Arquivos.gravar(fileName, head.toString(), false);


        for (int i = 0; i < issuesIds.size(); i++) {
            StringBuilder conteudo = new StringBuilder();
            Long id = issuesIds.get(i);
            Issue issue = this.getIssue(id);
            boolean[] vector = issue.getVectorLessAuthor(world, author);
            for (int j = 0; j < vector.length; j++) {
                conteudo.append(vector[j] ? 1 : 0).append(", ");
            }

            conteudo.append(issue.containsAuthor(author) ? 1 : 0);
            Arquivos.gravar(fileName, conteudo.toString(), true);
        }
    }

    private void createFiles(Author author, List<String> world, String trainingSet, String testSet) {
        StringBuilder head = new StringBuilder();
        head.append("@relation " + author.getNome() + "\n\n");
        for (int i = 0; i < world.size(); i++) {
            head.append("@attribute ");
            head.append(i);
            head.append(" {0,1}\n");
        }
        head.append("@attribute resultado {0, 1}\n");
        head.append("\n");
        head.append("@data\n");

        String fileName = String.format("arquivosGerados/%sTraining.arff", author.getNome());
        Arquivos.gravar(fileName, head.toString(), false);

        StringBuilder conteudo = new StringBuilder();
        String[] x = trainingSet.split("\n");
        for (int i = 0; i < x.length; i++) {
            conteudo.append(x[i] + "\n");
            if (i % 100 == 0) {
                Arquivos.gravarSemQuebra(fileName, conteudo.toString(), true);
                conteudo = new StringBuilder();
                System.gc();
            }
        }

        fileName = String.format("arquivosGerados/%sTest.arff", author.getNome());
        Arquivos.gravarSemQuebra(fileName, head.toString() + testSet, false);
    }

    private Author getAuthor(String name) {
        Author author = this.authors.get(name);
        if (author == null) {
            author = new Author();
            author.setNome(name);
            this.insertAuthor(author);
        }
        return author;
    }

    private void insertAuthor(Author author) {
        this.authors.put(author.getNome(), author);
    }

    private Message getMessage(long id) {
        for (int i = 0; i < this.messages.size(); i++) {
            if (id == this.messages.get(i).getId()) {
                return this.messages.get(i);
            }
        }
        return null;
    }

    private void insertMessage(Message m) {
        this.messages.add(m);
    }

    private Issue getIssue(long id) {
        Issue issue = this.issues.get(id);
        if (issue == null) {
            issue = new Issue();
            issue.setId(id);
            this.insertIssue(issue);
        }
        return issue;
    }

    private void insertIssue(Issue issue) {
        this.issues.put(issue.getId(), issue);
    }

    public static void conectar() throws Exception {
        con = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/hadoop",
                "root",
                "root");
    }

    public static void desconectar() throws Exception {
        con.close();
    }

    public Map<String, Author> getAuthors() {
        return authors;
    }

    public Map<Long, Issue> getIssues() {
        return issues;
    }

    public List<Message> getMessages() {
        return messages;
    }

    public static void main(String[] args) {
        World w = new World();
        //HDFS
        // w.geraWeka("tlipcon");
//        w.geraWeka("szetszwo");
//        w.geraWeka("sureshms");
//        w.geraWeka("eli");
//        w.geraWeka("dhruba");

        //Mapreduce
//        w.geraWeka("vinodkv");
//        w.geraWeka("acmurthy");
//        w.geraWeka("amareshwari");
//        w.geraWeka("mahadev");
//        w.geraWeka("tlipcon");

        //Hadoop

        w.geraWeka("szetszwo");
//        w.geraWeka("tlipcon");
//        w.geraWeka("dhruba");
//        w.geraWeka("rangadi");
//        w.geraWeka("chris.douglas");
//        w.geraWeka("cutting");
//        w.geraWeka("owen.omalley");



        System.out.println(w.issues.size());

    }
}
